package com.cwz.gateway.filter;

import com.cwz.core.constant.CacheConstants;
import com.cwz.core.constant.HttpStatus;
import com.cwz.core.constant.SecurityConstants;
import com.cwz.core.constant.TokenConstants;
import com.cwz.core.utils.jwt.JwtHS512Utils;
import com.cwz.core.utils.servle.ServletUtils;
import com.cwz.core.utils.string.StringUtils;
import com.cwz.gateway.config.properties.IgnoreWhiteProperties;
import com.cwz.redis.service.RedisService;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @program: w-demo
 * @description: 网关鉴权（用于 token 判断和请求头信息处理）
 * 全局过滤器是针对于网关而言通用的功能组件实现，采用全局来实现
 * @author: Wen
 **/
@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {
	// 排除过滤的 uri 地址，nacos自行添加
	@Autowired
	private IgnoreWhiteProperties ignoreWhiteProperties;
	@Autowired
	private RedisService redisService;

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		ServerHttpRequest request = exchange.getRequest();
		ServerHttpRequest.Builder mutate = request.mutate();
		String url = request.getURI().getPath();

		// 跳过不需要验证的路径
		if (StringUtils.matches(url, ignoreWhiteProperties.getWhites())) {
			return chain.filter(exchange);
		}
		String token = getToken(request);
		if (StringUtils.isEmpty(token)) {
			return unauthorizedResponse(exchange, "令牌不能为空");
		}
		Claims claims = JwtHS512Utils.parseToken(token);
		if (claims == null) {
			return unauthorizedResponse(exchange, "令牌已过期或验证不正确！");
		}
		String userKey = JwtHS512Utils.getUserKey(claims);
		Boolean isLogin = redisService.hasKey(getTokenKey(userKey));
		if (!isLogin) {
			return unauthorizedResponse(exchange, "登录状态已过期");
		}
		String userId = JwtHS512Utils.getUserId(claims);
		String userName = JwtHS512Utils.getUserName(claims);
		String company = JwtHS512Utils.getCompany(claims);
		if (StringUtils.isEmpty(userId) || StringUtils.isEmpty(userName)) {
			return unauthorizedResponse(exchange, "令牌验证失败");
		}

		// 设置用户信息到请求
		addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userId);
		addHeader(mutate, SecurityConstants.DETAILS_USERNAME, userName);
		addHeader(mutate, SecurityConstants.USER_KEY, userKey);
		addHeader(mutate, SecurityConstants.DETAILS_COMPANY, company);
		// 内部请求来源参数清除
		removeHeader(mutate, SecurityConstants.FROM_SOURCE);
		return chain.filter(exchange.mutate().request(mutate.build()).build());
	}

	@Override
	public int getOrder() {
		return -200;
	}

	private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) {
		if (value == null) {
			return;
		}
		String valueStr = value.toString();
		String valueEncode = ServletUtils.urlEncode(valueStr);
		mutate.header(name, valueEncode);
	}

	private void removeHeader(ServerHttpRequest.Builder mutate, String name) {
		mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
	}

	private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
		log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
		return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 获取缓存key
	 */
	private String getTokenKey(String token) {
		return CacheConstants.LOGIN_TOKEN_KEY + token;
	}

	/**
	 * 获取请求token
	 */
	private String getToken(ServerHttpRequest request) {
		String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
		// 如果前端设置了令牌前缀，则裁剪掉前缀
		if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {
			token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
		}
		return token;
	}
}
